//! @file   lc_scheduler.h
//! @brief  
//! @author Sayan Chaliha
//! @date   October, 2011
//!
//! 
#ifndef _LC_SCHEDULER_H
#define _LC_SCHEDULER_H

#include "libcyan_internal.h"
#include "lc_types.h"
#include "lc_object.h"
#include "lc_linked_list.h"
#include "lc_mutex.h"
#include "lc_condition_variable.h"
#include "lc_interval.h"
#include "lc_exception.h"
#include "lc_scope_guard.h"

namespace cyan {
    class CYAN_API SchedulerQueueEmptyException : public RuntimeException {
    public:
        explicit SchedulerQueueEmptyException(const String& s) :
            RuntimeException(s) {
        }
        
        virtual ~SchedulerQueueEmptyException() throw() {
        }
    };
    
    template <typename T>
    class CYAN_API Scheduler : public Object {
    public:
        typedef T               ValueType;
        typedef T*              Pointer;
        typedef const T*        ConstPointer;
        typedef T&              Reference;
        typedef const T&        ConstReference;
        
        Scheduler() : workQueueNotEmptyCondition_(workQueueMutex_) {
        }
        
        Scheduler(const Scheduler& other) :
            workQueueNotEmptyCondition_(workQueueMutex_) {
                CriticalSection guard(other.workQueueMutex_);
                this->workQueue_ = other.workQueue_;
        }
        
        virtual ~Scheduler() {
            this->clear();
        }
        
        virtual void enqueue(ValueType object) {
            CriticalSection section(this->workQueueMutex_);
            this->workQueue_.pushBack(object);
            this->workQueueNotEmptyCondition_.notify();
        }
        
        Size size() const {
            return this->workQueue_.size();
        }
        
        Bool isEmpty() const {
            return this->workQueue_.isEmpty();
        }
        
        void clear() {
            CriticalSection section(this->workQueueMutex_);
            this->workQueue_.clear();
        }
        
        Reference waitForNext(Interval interval) {
            CriticalSection section(this->workQueueMutex_);
            if (this->isEmpty())
                if (!this->workQueueNotEmptyCondition_.wait(interval))
                    Exception::throwException
                        (TimedOutException(TEXT("Scheduler::waitForNext")));
            return this->policyTarget_();
        }
        
        virtual Reference next() {
            CriticalSection section(this->workQueueMutex_);
            if (this->isEmpty())
                Exception::throwException
                    (SchedulerQueueEmptyException(TEXT("Scheduler::next")));
            return this->policyTarget_();
        }
        
        Scheduler& copy(const Scheduler& other) {
            CriticalSection otherGuard(other.workQueueMutex_);
            CriticalSection myGuard(this->workQueueMutex_);
            
            this->workQueue_ = other.workQueue_;
            return *this;
        }
        
        Scheduler& operator =(const Scheduler& other) {
            this->copyQueue(other);
        }
        
    protected:
        virtual ValueType& policyTarget_() = 0;
        
        Mutex workQueueMutex_;
        ConditionVariable workQueueNotEmptyCondition_;
        LinkedList<T> workQueue_;
    };
    
    template <typename T>
    class CYAN_API FifoScheduler : public Scheduler<T> {
    public:
        typedef typename Scheduler<T>::ValueType        ValueType;
        typedef typename Scheduler<T>::Pointer          Pointer;
        typedef typename Scheduler<T>::ConstPointer     ConstPointer;
        typedef typename Scheduler<T>::Reference        Reference;
        typedef typename Scheduler<T>::ConstReference   ConstReference;
        
        FifoScheduler() {
        }
        
        virtual ~FifoScheduler() {
        }
        
    private:
        virtual ValueType& policyTarget_() {
            ScopeGuard guard(bind(&this->workQueue_,
                                  reinterpret_cast<void (LinkedList<T>::*)()>
                                  (&LinkedList<T>::popFront)));
            
            return this->workQueue_.front();
        }
    };
    
    template <typename T>
    class CYAN_API LifoScheduler : public Scheduler<T> {
    public:
        typedef typename Scheduler<T>::ValueType        ValueType;
        typedef typename Scheduler<T>::Pointer          Pointer;
        typedef typename Scheduler<T>::ConstPointer     ConstPointer;
        typedef typename Scheduler<T>::Reference        Reference;
        typedef typename Scheduler<T>::ConstReference   ConstReference;
        
        LifoScheduler() {
        }
        
        virtual ~LifoScheduler() {
        }
        
    private:
        virtual Reference policyTarget_() {
            ScopeGuard guard(bind(&this->workQueue_,
                                  reinterpret_cast<void (LinkedList<T>::*)()>
                                  (&LinkedList<T>::popBack)));
            
            return this->workQueue_.back();
        }
    };
}

#endif /* _LC_SCHEDULER_H */
